package cn.blu10ph.trustshare.service.impl;

import cn.blu10ph.trustshare.bean.msg.BaseMsg;
import cn.blu10ph.trustshare.bean.msg.FindNodeMsg;
import cn.blu10ph.trustshare.bean.msg.NodesMsg;
import cn.blu10ph.trustshare.bean.msg.TrustMsg;
import cn.blu10ph.trustshare.bean.node.GetPublicKeyMsg;
import cn.blu10ph.trustshare.bean.node.TrustNode;
import cn.blu10ph.trustshare.constant.Constant;
import cn.blu10ph.trustshare.core.PGPManager;
import cn.blu10ph.trustshare.service.DHTMsgService;
import cn.blu10ph.trustshare.util.ConvertUtil;
import cn.blu10ph.trustshare.util.PgpUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.List;

/**
 * <p> DHTFindNodeMsgServiceImpl </p >
 *
 * @author cxx
 * @date 2024/4/6 22:22
 */
@Slf4j
@Component
public class DHTFindNodeMsgServiceImpl extends DHTMsgBaseService<FindNodeMsg> implements DHTMsgService<FindNodeMsg> {

    @Resource
    DHTGetPublicKeyMsgServiceImpl getPublicKeyMsgServiceImpl;

    @Override
    public void sendQuery(String nodeId, String hostname, int port, FindNodeMsg params) throws Exception {
        if(ObjectUtils.isEmpty(params)){
            String errMsg = getType() + " msg params is empty";
            log.error(errMsg);
            throw new Exception(errMsg);
        }
        if(!StringUtils.hasText(params.getTarget())){
            String errMsg = getType() + " msg targetNodeId is empty";
            log.error(errMsg);
            throw new Exception(errMsg);
        }
        super.sendQuery(nodeId, hostname, port, params);
    }

    @Override
    public void sendReturn(ChannelHandlerContext ctx, DatagramPacket packet, TrustNode node, TrustMsg msg) {
        FindNodeMsg findNodeMsg;
        try {
            findNodeMsg = ConvertUtil.str2Obj(msg.getParams(), FindNodeMsg.class);
        } catch (JsonProcessingException ex) {
            ex.printStackTrace();
            return;
        }

        BaseMsg msgObj = getBaseMsg(Constant.MSG_DIRECTION_RETURN, node.getNodeId());
        List<TrustNode> nodes = routingTable.findNodeListById(findNodeMsg.getTarget());
        NodesMsg nodesMsg = new NodesMsg(findNodeMsg);
        nodesMsg.setNodes(nodes);
        try {
            msgObj.setParams(ConvertUtil.obj2Str(nodesMsg));
            response(node, ctx, packet.sender(), msgObj);
        } catch (JsonProcessingException ex) {
            ex.printStackTrace();
        }
    }

    @Override
    public void receive(ChannelHandlerContext ctx, DatagramPacket packet, TrustNode node, TrustMsg msg) {
        NodesMsg nodesMsg;
        try {
            nodesMsg = ConvertUtil.str2Obj(msg.getParams(), NodesMsg.class);
        } catch (JsonProcessingException ex) {
            ex.printStackTrace();
            return;
        }
        List<TrustNode> nodes = nodesMsg.getNodes();

        nodes.forEach(nodeItem->{
            // node is have check
            TrustNode nodeTemp = routingTable.getNodeById(nodeItem.getNodeId());
            if(!ObjectUtils.isEmpty(nodeTemp)){
                return;
            }

            // send get_public_key msg
            try {
                getPublicKeyMsgServiceImpl.sendQuery(nodeItem, new GetPublicKeyMsg(
                        nodeItem.getNodeId(), routingTable.getSelfNode(),
                        PgpUtil.getPublicKeyEncode(PgpUtil.getPublicKey(PGPManager.getSecretKey()))
                ));
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        });
    }

    @Override
    protected Class<FindNodeMsg> getParamsType() {
        return FindNodeMsg.class;
    }

    @Override
    public String getType() {
        return Constant.MSG_TYPE_FIND_NODE;
    }

}
